package org.example.study5;
/**
 * 创建两个线程
 * 1. 第一个线程，不停循环去执行自己的任务
 * 2. 第二个线程，输入一个停止标识，使第一个线程退出
 */
import java.util.Scanner;

public class Demo_502 {
    // 定义退出标识（静态变量，两个线程共享）
    static int flag = 0;

    public static void main(String[] args) {

        // 创建执行任务的线程 t1（长期运行的任务）
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "线程启动.");
            // 循环检测退出标识（当flag为0时持续运行）
            while (flag == 0) {
                // 空循环模拟任务处理
            }
            System.out.println(Thread.currentThread().getName() + "线程退出.");
        }, "t1");
        t1.start(); // 启动t1线程

        // 创建控制线程 t2（用于接收用户输入）
        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "线程启动.");
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个非零的整数：");
            // 修改共享变量（可能因为可见性问题导致t1无法立即感知）
            flag = scanner.nextInt();
            System.out.println(Thread.currentThread().getName() + "线程退出.");
        }, "t2");
        t2.start(); // 启动t2线程
    }
}


/*运行：
t1线程启动.
t2线程启动.
请输入一个非零的整数：
1
t2线程退出.*/

/* 现象：
t2线程正常结束，并且已经修改了flag变量的值，但是t1没有结束，整个进程也没有结束，结果不及预期，线程安全问题产生。
*/

/* 原因
对于线程t1来说，只是比较flag这个变量的值，从来都没有修改过，所以CPU认为，这个值永远也不会改变，从而也不会重新从主主存中读取值
为了提高运行效率这个值一般存在寄存器或CPU的缓存中

t2线程是正常修改的，但是线程t2的修改，线程t1无法感知到。这就是因为内存可见性的问题。
从而也知道为了解决内存可见性问题，最重要的是当一个线程修改了另一个线程需要的变量，必须让另一个线程感知到。
*/

/*解决方式：给共享变量加volatile修饰。用volatile修饰过的变量，由于前后有内存屏障，保证了指令的执行顺序。也可以理解为告诉编译器，不要进行指令重排
static volatile int flag = 0;*/

/*总结：
* volatile
1.解决了内存可见性
2.解决了有序性
3.不保证原子性
*
* JMM如何实现原子性，可见性，有序性？
synchronized实现原子性，由于是串行从而也实现了可见性
volatile真正实现了内存可见性（使用了内存屏障），有序性

*/

